import os
import sys
import uuid
import time
import logging
import configparser
import subprocess
import hashlib
from datetime import datetime
from typing import List, Optional, Dict, Any

from fastapi import FastAPI, File, UploadFile, HTTPException, Form, BackgroundTasks, Depends
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
import win32print
from pydantic import BaseModel

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(f"logs/api_server_{datetime.now().strftime('%Y%m%d')}.log"),
        logging.StreamHandler()
    ]
)

# 创建FastAPI应用
app = FastAPI(title="文档打印API服务器", description="支持上传和打印各种文档类型")

# 允许跨域请求
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 配置静态文件
os.makedirs("static", exist_ok=True)
os.makedirs("static/css", exist_ok=True)
os.makedirs("static/js", exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static")

# 配置模板引擎
templates = Jinja2Templates(directory="templates")

# 加载配置
def load_config():
    config = configparser.ConfigParser()
    if not os.path.exists('config.ini'):
        raise FileNotFoundError("配置文件 config.ini 不存在")
        
    config.read('config.ini', encoding='utf-8')
    
    # 从配置文件获取允许的扩展名
    supported_extensions = config.get('Settings', 'supported_extensions', 
                                      fallback='.pdf, .doc, .docx').split(',')
    supported_extensions = [ext.strip().lower() for ext in supported_extensions]
    
    # 获取默认打印机
    default_printer = config.get('Settings', 'default_printer', 
                                fallback=win32print.GetDefaultPrinter())
    
    # 获取其他配置
    watch_folder = config.get('Settings', 'watch_folder', fallback='watch_folder')
    temp_folder = config.get('Settings', 'temp_folder', fallback='temp_prints')
    max_retries = config.getint('Settings', 'max_retries', fallback=3)
    retry_interval = config.getint('Settings', 'retry_interval', fallback=5)
    
    # 创建必要的目录
    os.makedirs(watch_folder, exist_ok=True)
    os.makedirs(temp_folder, exist_ok=True)
    os.makedirs('uploads', exist_ok=True)
    os.makedirs('logs', exist_ok=True)
    
    return {
        'supported_extensions': supported_extensions,
        'default_printer': default_printer,
        'watch_folder': watch_folder,
        'temp_folder': temp_folder,
        'max_retries': max_retries,
        'retry_interval': retry_interval,
        'max_file_size_mb': 20  # 默认最大文件大小为20MB
    }

# 获取配置的依赖函数
def get_config():
    return load_config()

# 数据模型
class PrintRequest(BaseModel):
    filename: str
    printer: Optional[str] = None

class PrintServiceStatus(BaseModel):
    status: str
    message: str

# 全局变量
UPLOAD_FOLDER = "uploads"
print_history = []
printed_file_hashes = {}  # 存储已打印文件的哈希值

# 主页路由
@app.get("/", response_class=HTMLResponse)
async def home(config: dict = Depends(get_config)):
    try:
        return templates.TemplateResponse("index.html", {"request": {}})
    except Exception as e:
        logging.error(f"渲染主页时出错: {str(e)}")
        raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")

# 获取配置信息
@app.get("/config")
async def get_server_config(config: dict = Depends(get_config)):
    return {
        "allowed_extensions": config["supported_extensions"],
        "default_printer": config["default_printer"],
        "max_file_size_mb": config["max_file_size_mb"]
    }

# 获取打印机列表
@app.get("/printers")
async def get_printers():
    try:
        printers = [printer[2] for printer in 
                   win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | 
                                          win32print.PRINTER_ENUM_CONNECTIONS)]
        default_printer = win32print.GetDefaultPrinter()
        return {
            "printers": printers,
            "default": default_printer
        }
    except Exception as e:
        logging.error(f"获取打印机列表时出错: {str(e)}")
        raise HTTPException(status_code=500, detail=f"获取打印机列表失败: {str(e)}")

# 检查文件扩展名是否允许
def allowed_file(filename, config):
    return '.' in filename and \
           os.path.splitext(filename)[1].lower() in config["supported_extensions"]

# 生成唯一文件名
def generate_unique_filename(original_filename):
    _, ext = os.path.splitext(original_filename)
    return f"{uuid.uuid4()}{ext}"

# 上传文件
@app.post("/upload")
async def upload_file(file: UploadFile = File(...), config: dict = Depends(get_config)):
    try:
        if not allowed_file(file.filename, config):
            raise HTTPException(status_code=400, 
                              detail=f"不支持的文件类型，允许的类型: {', '.join(config['supported_extensions'])}")
        
        # 检查文件大小
        file_size = 0
        chunk_size = 1024 * 1024  # 1MB
        content = bytearray()
        
        while True:
            chunk = await file.read(chunk_size)
            if not chunk:
                break
            content.extend(chunk)
            file_size += len(chunk)
            
            # 检查文件大小是否超过限制
            if file_size > config["max_file_size_mb"] * 1024 * 1024:
                raise HTTPException(status_code=400, 
                                  detail=f"文件太大，最大允许大小: {config['max_file_size_mb']}MB")
        
        # 重置文件指针
        await file.seek(0)
        
        # 保存文件
        unique_filename = generate_unique_filename(file.filename)
        file_path = os.path.join(UPLOAD_FOLDER, unique_filename)
        
        with open(file_path, "wb") as f:
            f.write(content)
        
        original_filename = file.filename
        
        # 记录上传历史
        upload_record = {
            "id": str(uuid.uuid4()),
            "original_filename": original_filename,
            "saved_filename": unique_filename,
            "size": file_size,
            "upload_time": datetime.now().isoformat(),
            "status": "uploaded"
        }
        
        print_history.append(upload_record)
        
        logging.info(f"文件上传成功: {original_filename} -> {unique_filename}")
        
        return {
            "message": "文件上传成功",
            "filename": unique_filename,
            "original_filename": original_filename,
            "size": file_size
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"文件上传失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}")

# 获取已上传文件列表
@app.get("/files")
async def get_files():
    try:
        return {"files": print_history}
    except Exception as e:
        logging.error(f"获取文件列表失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"获取文件列表失败: {str(e)}")

# 计算文件哈希值
def calculate_file_hash(file_path):
    """计算文件的MD5哈希值"""
    try:
        if not os.path.exists(file_path):
            return None
            
        hasher = hashlib.md5()
        with open(file_path, 'rb') as f:
            buf = f.read(65536)  # 读取64kb块
            while len(buf) > 0:
                hasher.update(buf)
                buf = f.read(65536)
        return hasher.hexdigest()
    except Exception as e:
        logging.error(f"计算文件哈希值失败: {str(e)}")
        return None

# 打印文件
@app.post("/print_file")
async def print_file(request: PrintRequest, config: dict = Depends(get_config)):
    try:
        # 获取文件路径
        file_path = os.path.join(UPLOAD_FOLDER, request.filename)
        
        if not os.path.exists(file_path):
            raise HTTPException(status_code=404, detail="文件不存在")
        
        # 获取打印机
        printer_name = request.printer if request.printer else config["default_printer"]
        
        # 检查文件是否已在打印队列或已打印
        file_status = None
        for record in print_history:
            if record.get("saved_filename") == request.filename:
                file_status = record.get("status")
                break
        
        if file_status in ["queued", "printing"]:
            return {
                "message": "文件已在打印队列中",
                "filename": request.filename,
                "printer": printer_name,
                "status": file_status
            }
        
        # 计算文件哈希值，防止重复打印相同内容的文件
        file_hash = calculate_file_hash(file_path)
        if file_hash and file_hash in printed_file_hashes:
            previous_record = printed_file_hashes[file_hash]
            logging.info(f"检测到重复内容文件: {request.filename} 与 {previous_record['filename']} 内容相同")
            
            # 更新当前文件的状态
            for record in print_history:
                if record.get("saved_filename") == request.filename:
                    record["print_time"] = datetime.now().isoformat()
                    record["printer"] = printer_name
                    record["status"] = "printed"  # 标记为已打印
                    record["note"] = f"内容与文件 {previous_record['filename']} 相同，无需重复打印"
                    break
            
            return {
                "message": "文件内容已打印过",
                "filename": request.filename,
                "duplicate_of": previous_record['filename'],
                "status": "printed"
            }
        
        # 复制文件到监视文件夹
        watch_folder = config["watch_folder"]
        dest_path = os.path.join(watch_folder, request.filename)
        
        with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
            dst.write(src.read())
        
        # 记录打印历史和文件哈希
        for record in print_history:
            if record.get("saved_filename") == request.filename:
                record["print_time"] = datetime.now().isoformat()
                record["printer"] = printer_name
                record["status"] = "queued"
                
                # 记录哈希值
                if file_hash:
                    record["file_hash"] = file_hash
                    printed_file_hashes[file_hash] = {
                        "filename": request.filename,
                        "original_filename": record.get("original_filename", ""),
                        "print_time": record["print_time"]
                    }
                break
        
        logging.info(f"文件 {request.filename} 已加入打印队列, 目标打印机: {printer_name}")
        
        return {
            "message": "文件已加入打印队列",
            "filename": request.filename,
            "printer": printer_name
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"打印文件失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"打印文件失败: {str(e)}")

# 后台任务：启动打印服务
def start_print_service_task():
    try:
        logging.info("正在启动打印服务...")
        # 使用subprocess启动打印服务程序
        process = subprocess.Popen([sys.executable, "print_service.py"],
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
        
        # 等待进程启动
        time.sleep(2)
        
        if process.poll() is not None:
            # 进程已终止
            stdout, stderr = process.communicate()
            logging.error(f"打印服务启动失败: {stderr.decode('utf-8')}")
            return False
        
        logging.info("打印服务已成功启动")
        return True
    
    except Exception as e:
        logging.error(f"启动打印服务时出错: {str(e)}")
        return False

# 启动打印服务
@app.post("/start_print_service", response_model=PrintServiceStatus)
async def start_print_service(background_tasks: BackgroundTasks):
    try:
        background_tasks.add_task(start_print_service_task)
        return {
            "status": "success",
            "message": "正在启动打印服务"
        }
    except Exception as e:
        logging.error(f"启动打印服务请求失败: {str(e)}")
        return {
            "status": "error",
            "message": f"启动打印服务失败: {str(e)}"
        }

# 重启打印服务
@app.post("/restart_print_service", response_model=PrintServiceStatus)
async def restart_print_service(background_tasks: BackgroundTasks):
    try:
        logging.info("接收到重启打印服务请求")
        
        # 首先尝试关闭任何正在运行的打印服务进程
        try:
            # 在Windows上使用taskkill命令终止进程
            subprocess.run(["taskkill", "/F", "/IM", "print_service.py"], 
                         stdout=subprocess.PIPE, 
                         stderr=subprocess.PIPE,
                         shell=True)
            logging.info("已发送终止打印服务请求")
            
            # 等待进程完全关闭
            time.sleep(2)
        except Exception as e:
            logging.warning(f"关闭现有打印服务时出错: {str(e)}")
        
        # 启动新的打印服务进程
        background_tasks.add_task(start_print_service_task)
        
        return {
            "status": "success",
            "message": "打印服务正在重启"
        }
    except Exception as e:
        logging.error(f"重启打印服务请求失败: {str(e)}")
        return {
            "status": "error",
            "message": f"重启打印服务失败: {str(e)}"
        }

# 定义404错误处理
@app.exception_handler(404)
async def not_found_exception_handler(request, exc):
    return templates.TemplateResponse("troubleshooting.html", 
                                   {"request": request, "error": "页面不存在"}, 
                                   status_code=404)

# 定义500错误处理
@app.exception_handler(500)
async def server_error_exception_handler(request, exc):
    return templates.TemplateResponse("troubleshooting.html", 
                                   {"request": request, "error": str(exc)}, 
                                   status_code=500)

# 主入口
if __name__ == "__main__":
    # 确保所有目录存在
    config = load_config()
    
    print(f"=== 文档打印API服务器 ===")
    print(f"服务器启动在: http://localhost:8000")
    print(f"支持的文件类型: {', '.join(config['supported_extensions'])}")
    print(f"默认打印机: {config['default_printer']}")
    
    # 启动服务器
    uvicorn.run(app, host="0.0.0.0", port=8000) 